package com.faner4cloud.yun.common.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Java8 StreamAPI 常用方法封装
 *
 */
public class StreamUtil {
	/**
	 * 集合转换为List
	 *
	 * @param collection  集合对象
	 * @param valueMapper value映射规则
	 * @param <E>         集合元素类型的泛型
	 * @param <V>         最终输出类型的泛型
	 * @return List<V>
	 */
	public static <E, V> List<V> mapList(Collection<E> collection, Function<E, V> valueMapper) {
		if (collection == null) {
			return null;
		}
		return collection.stream().map(valueMapper).collect(Collectors.toList());
	}

	/**
	 * 集合转换为Set
	 *
	 * @param collection  集合对象
	 * @param valueMapper value映射规则
	 * @param <E>         集合元素类型的泛型
	 * @param <V>         最终输出类型的泛型
	 * @return Set<V>
	 */
	public static <E, V> Set<V> mapSet(Collection<E> collection, Function<E, V> valueMapper) {
		if (collection == null) {
			return null;
		}
		return collection.stream().map(valueMapper).collect(Collectors.toSet());
	}

	/**
	 * 集合转换为Map
	 *
	 * @param collection  集合对象
	 * @param keyMapper   Key的映射规则
	 * @param valueMapper value映射规则
	 * @param <K>         Key的泛型
	 * @param <E>         集合元素类型的泛型
	 * @param <V>         最终输出类型的泛型
	 * @return Map<K, V>
	 */
	public static <K, E, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> keyMapper, Function<E, V> valueMapper) {
		if (collection == null) {
			return null;
		}
		return collection.stream().collect(Collectors.toMap(keyMapper, valueMapper, (v1, v2) -> v2));
	}

	public static <K, E> Map<K, E> toMap(Collection<E> collection, Function<E, K> keyMapper) {
		return toMap(collection, keyMapper, Function.identity());
	}

	/**
	 * 按照指定Key进行分组
	 *
	 * @param collection 集合对象
	 * @param keyMapper  Key的映射规则
	 * @param <K>        Key的泛型
	 * @param <E>        集合元素类型的泛型
	 * @return Map<K, List < E>>
	 */
	public static <K, E> Map<K, List<E>> groupBy(Collection<E> collection, Function<E, K> keyMapper) {
		if (collection == null) {
			return null;
		}
		return collection.stream().collect(Collectors.groupingBy(keyMapper, Collectors.toList()));
	}

	/**
	 * 集合根据指定的Key规则进行分组求最大值（比如：按照用户ID分组，取每个用户的最近的订单数据 或者 按照用户ID分组，统计每个用户某个属性的最大值）
	 *
	 * @param collection  集合对象
	 * @param keyMapper   分组Key的映射规则
	 * @param valueMapper value映射规则
	 * @param comparator  集合合并的比较器
	 * @return Map<K, V>
	 */
	public static <K, E, V> Map<K, V> maxByGroup(Collection<E> collection, Function<E, K> keyMapper, Function<E, V> valueMapper, Comparator<V> comparator) {
		return merge(collection, keyMapper, valueMapper, BinaryOperator.maxBy(comparator));
	}

	public static <K, E> Map<K, E> maxByGroup(Collection<E> collection, Function<E, K> keyMapper, Comparator<E> comparator) {
		return merge(collection, keyMapper, Function.identity(), BinaryOperator.maxBy(comparator));
	}

	/**
	 * 集合根据指定的Key规则进行分组求最小值（比如：按照用户ID分组，取每个用户的最早的历史数据 或者 按照用户ID分组，统计每个用户某个属性的最小值）
	 *
	 * @param collection  集合对象
	 * @param keyMapper   分组Key的映射规则
	 * @param valueMapper value映射规则
	 * @param comparator  集合合并的比较器
	 * @return Map<K, V>
	 */
	public static <K, E, V> Map<K, V> minByGroup(Collection<E> collection, Function<E, K> keyMapper, Function<E, V> valueMapper, Comparator<V> comparator) {
		return merge(collection, keyMapper, valueMapper, BinaryOperator.minBy(comparator));
	}

	public static <K, E> Map<K, E> minByGroup(Collection<E> collection, Function<E, K> keyMapper, Comparator<E> comparator) {
		return merge(collection, keyMapper, Function.identity(), BinaryOperator.minBy(comparator));
	}

	/**
	 * 集合对象的最大值
	 *
	 * @param collection 集合对象
	 * @param comparator 集合合并的比较器
	 * @return E
	 */
	public static <E> E max(Collection<E> collection, Comparator<E> comparator) {
		if (collection == null) {
			return null;
		}
		Optional<E> maxResult = collection.stream().reduce(BinaryOperator.maxBy(comparator));
		return maxResult.orElse(null);
	}

	/**
	 * 集合对象的最小值
	 *
	 * @param collection 集合对象
	 * @param comparator 集合合并的比较器
	 * @return E
	 */
	public static <E> E min(Collection<E> collection, Comparator<E> comparator) {
		if (collection == null) {
			return null;
		}
		Optional<E> minResult = collection.stream().reduce(BinaryOperator.minBy(comparator));
		return minResult.orElse(null);
	}

	/**
	 * 集合根据指定的Key规则进行分组求和（比如：按照用户ID分组，取每个用户的余额总值）
	 *
	 * @param collection  集合对象
	 * @param keyMapper   分组Key的映射规则
	 * @param valueMapper value映射规则
	 * @return Map<K, Integer>
	 */
	public static <K, E> Map<K, Integer> sumIntegerByGroup(Collection<E> collection, Function<E, K> keyMapper, Function<E, Integer> valueMapper) {
		return merge(collection, keyMapper, valueMapper, Integer::sum);
	}

	public static <K, E> Map<K, Long> sumLongByGroup(Collection<E> collection, Function<E, K> keyMapper, Function<E, Long> valueMapper) {
		return merge(collection, keyMapper, valueMapper, Long::sum);
	}

	public static <K, E> Map<K, Double> sumDoubleByGroup(Collection<E> collection, Function<E, K> keyMapper, Function<E, Double> valueMapper) {
		return merge(collection, keyMapper, valueMapper, Double::sum);
	}

	/**
	 * 集合根据指定的Key规则进行分组计数（比如：按照订单状态分组，统计订单的条数）
	 *
	 * @param collection 集合对象
	 * @param keyMapper  分组Key的映射规则
	 * @return Map<K, Long>
	 */
	public static <K, E> Map<K, Long> countingByGroup(Collection<E> collection, Function<E, K> keyMapper) {
		if (collection == null) {
			return null;
		}
		if (collection.isEmpty()) {
			return new HashMap<>();
		}
		return collection.stream().collect(Collectors.groupingBy(keyMapper, Collectors.counting()));
	}

	/**
	 * 集合根据指定的Key规则进行分组合并
	 *
	 * @param collection     集合对象
	 * @param keyMapper      分组Key的映射规则
	 * @param valueMapper    value映射规则
	 * @param binaryOperator 自定义集合合并的规则
	 * @param <K>            Key的泛型
	 * @param <E>            集合元素类型的泛型
	 * @param <V>            最终输出类型的泛型
	 * @return Map<K, V>
	 */
	public static <K, E, V> Map<K, V> merge(Collection<E> collection, Function<E, K> keyMapper, Function<E, V> valueMapper, BinaryOperator<V> binaryOperator) {
		if (collection == null) {
			return null;
		}
		if (collection.isEmpty()) {
			return new HashMap<>();
		}
		return collection.stream().collect(Collectors.toMap(keyMapper, valueMapper, binaryOperator));
	}

	/**
	 * 集合元素求和
	 *
	 * @param collection 集合对象
	 * @return Integer
	 */
	public static int sumInteger(Collection<Integer> collection) {
		return sum(collection, Integer::sum, 0);
	}

	public static long sumLong(Collection<Long> collection) {
		return sum(collection, Long::sum, 0L);
	}

	public static double sumDouble(Collection<Double> collection) {
		return sum(collection, Double::sum, 0.d);
	}

	/**
	 * 集合元素求和
	 *
	 * @param collection     集合对象
	 * @param keyMapper      需要求和的属性映射
	 * @param binaryOperator 函数式接口，取两个值并产生一个新值
	 * @param defaultResult  默认值或初始值。
	 * @return U
	 */
	public static <E, U> U sum(Collection<E> collection, Function<E, U> keyMapper, BinaryOperator<U> binaryOperator, U defaultResult) {
		if (collection == null) {
			return defaultResult;
		}
		return collection.stream().map(keyMapper).reduce(defaultResult, binaryOperator);
	}

	public static <E> E sum(Collection<E> collection, BinaryOperator<E> binaryOperator, E defaultResult) {
		return sum(collection, Function.identity(), binaryOperator, defaultResult);
	}


	public static void main(String[] args) {
		class User {
			private String name;
			private int count;
			private String date;

			private User(String name, int count, String date) {
				this.name = name;
				this.count = count;
				this.date = date;
			}

			private String getName() {
				return name;
			}

			private int getCount() {
				return count;
			}

			private String getDate() {
				return date;
			}

			@Override
			public String toString() {
				return "User{" +
					"name='" + name + '\'' +
					", count=" + count +
					", date='" + date + '\'' +
					'}';
			}
		}
		List<User> list = Arrays.asList(
			new User("张三", 1, "2020-12-20")
			, new User("张三", 2, "2020-12-21")

			, new User("李四", 3, "2020-12-22")
			, new User("李四", 4, "2020-12-24"),

			new User("王五", 100, "2020-12-27"));


		System.out.println(maxByGroup(list, User::getName, Comparator.comparing(User::getDate)));
		System.out.println(minByGroup(list, User::getName, Comparator.comparing(User::getDate)));
		System.out.println(sumIntegerByGroup(list, User::getName, User::getCount));

		System.out.println("======================================");
		System.out.println(maxByGroup(new ArrayList<>(), User::getName, Comparator.comparing(User::getDate)));
		System.out.println(toMap(new ArrayList<>(), User::getName));
		System.out.println(mapList(list, User::getName));
		System.out.println(mapSet(list, User::getName));

		System.out.println("=======================================");
		System.out.println(sumInteger(Arrays.asList(6, 9)));
		System.out.println(sum(list, User::getCount, Integer::sum, 0));
		System.out.println(sum(Arrays.asList(new BigDecimal(2), new BigDecimal(6)), BigDecimal::add, BigDecimal.ZERO));

	}
}
